﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MadLibrary.Math
{
    /// <summary>
    /// Represents fraction in code.
    /// </summary>
    public class Fraction
    {
        /// <summary>
        /// Gets or sets numerator of current fraction.
        /// </summary>
        public int Numerator { get; set; }
        /// <summary>
        /// Gets or sets Denominator of current fraction.
        /// </summary>
        public uint Denominator { get; set; }

        /// <summary>
        /// Gets string form of current fraction.
        /// </summary>
        /// <returns>String form of current fraction.</returns>
        public override string ToString()
        {
                return string.Format("{0}/{1}", this.Numerator, this.Denominator);
        }
        /// <summary>
        /// Gets hash code of current fraction.
        /// </summary>
        /// <returns>Hash code of current fraction.</returns>
        public override int GetHashCode()
        {
            return (int)(this.Numerator * 0.159 + this.Denominator * 0.951 + this.ToString().Length);
        }
        /// <summary>
        /// Compares current fraction with specified object.
        /// </summary>
        /// <param name="obj">Specifies object with has to be compared with current fraction.</param>
        /// <returns>Comparasion result.</returns>
        public override bool Equals(object obj)
        {
            if (obj is Fraction)
            {
                Fraction f = obj as Fraction;
                if (f.Numerator == this.Numerator && this.Denominator == f.Denominator)
                    return true;
                else return false;
            }
            else
                return false;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <param name="s">The s.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static bool operator==(Fraction f, Fraction s)
        {
            return f.Equals(s);
        }
        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <param name="s">The s.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static bool operator !=(Fraction f, Fraction s)
        {
            if (f.Equals(s))
                return false;
            else return true;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.Int32"/> to <see cref="MadLibrary.Math.Fraction"/>.
        /// </summary>
        /// <param name="integer">The integer.</param>
        /// <returns>
        /// The result of the conversion.
        /// </returns>
        public static implicit operator Fraction(int integer)
        {
            return new Fraction(integer);
        }
        /// <summary>
        /// Performs an implicit conversion from <see cref="MadLibrary.Math.Fraction"/> to <see cref="System.Double"/>.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <returns>
        /// The result of the conversion.
        /// </returns>
        public static implicit operator double(Fraction f)
        {
            return ((double)f.Numerator / (double)f.Denominator);
        }

        /// <summary>
        /// Performs an explicit conversion from <see cref="System.Double"/> to <see cref="MadLibrary.Math.Fraction"/>.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <returns>
        /// The result of the conversion.
        /// </returns>
        public static explicit operator Fraction(double number)
        {
            return Fraction.FromDecimal(number, 1.0);
        }
        /// <summary>
        /// Performs an implicit conversion from <see cref="MadLibrary.Math.Fraction"/> to <see cref="System.String"/>.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <returns>
        /// The result of the conversion.
        /// </returns>
        public static implicit operator string(Fraction f)
        {
            return f.ToString();
        }

        /// <summary>
        /// Reducts numerator and Denominator of fraction.
        /// </summary>
        public void Reduct()
        {
            uint res = 0;
            int highter = this.Numerator;
            if (this.Numerator < this.Denominator)
                highter = (int)this.Denominator;
            for (uint i = 2; i <= highter; i++)
            {
                if (this.Numerator % i == 0 && this.Denominator % i == 0)
                {
                    res = (uint)i;
                    break;
                }
            }
            if (res != 0)
            {
                this.Numerator /= (int)res;
                this.Denominator /= res;
                this.Reduct();
            }
        }
        /// <summary>
        /// Expands numerator and Denominator of fraction.
        /// </summary>
        /// <param name="number">Number which will be used for fraction expanadation.</param>
        public void Expand(int number)
        {
            this.Numerator *= number;
            this.Denominator *= (uint)number;
        }

        /// <summary>
        /// Gets new fraction from the specified decimal numbers.
        /// </summary>
        /// <param name="Numerator">The numerator.</param>
        /// <param name="Denominator">The denominator.</param>
        /// <returns>Calculated fraction.</returns>
        public static Fraction FromDecimal(double Numerator, double Denominator)
        {
            while (Numerator != System.Math.Round(Numerator) || Denominator != System.Math.Round(Denominator))
            {
                Numerator *= 10;
                Denominator *= 10;
            }
            Fraction f = new Fraction();
            f.Numerator = (int)Numerator;
            f.Denominator = (uint)Denominator;
            f.Reduct();
            return f;
        }
        /// <summary>
        /// Gets new fraction from the specified normal numbers.
        /// </summary>
        /// <param name="Numerator">The numerator.</param>
        /// <param name="Denominator">The denominator.</param>
        /// <returns>Calculated fraction.</returns>
        public static Fraction FromNormal(int Numerator, uint Denominator)
        {
            return new Fraction(Numerator, Denominator);
        }
        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <param name="s">The s.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static Fraction operator +(Fraction f, Fraction s)
        {
            Fraction ret = new Fraction();
            uint dmal = f.Denominator * s.Denominator;
            double num = (dmal / f.Denominator * f.Numerator) + (dmal / s.Denominator * s.Numerator);
            ret = Fraction.FromDecimal(num, dmal);
            return ret;
        }
        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <param name="s">The s.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static Fraction operator -(Fraction f, Fraction s)
        {
            Fraction ret = new Fraction();
            double dmal = f.Denominator * s.Denominator;
            double res = (dmal / f.Denominator * f.Numerator) - (dmal / s.Denominator * s.Numerator);
            ret = Fraction.FromDecimal(res, dmal);
            return ret;
        }
        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <param name="s">The s.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static Fraction operator *(Fraction f, Fraction s)
        {
            Fraction ret = new Fraction();
            ret.Numerator = f.Numerator * s.Numerator;
            ret.Denominator = f.Denominator * s.Denominator;
            ret.Reduct();
            return ret;
        }
        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="f">The f.</param>
        /// <param name="s">The s.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static Fraction operator /(Fraction f, Fraction s)
        {
            Fraction ret = new Fraction();
            ret.Numerator = (int)(f.Numerator * s.Denominator);
            ret.Denominator = (uint)(f.Denominator * s.Numerator);
            ret.Reduct();
            return ret;
        }

        /// <summary>
        /// Creates new empty fraction.
        /// </summary>
        public Fraction()
        {
            this.Numerator = 0;
            this.Denominator = 0;
        }
        /// <summary>
        /// Creates new fraction from specified number.
        /// </summary>
        /// <param name="number">Specifies number which should be used for numerator and Denominator.</param>
        public Fraction(int number)
        {
            number += number * 2;
            this.Numerator = (int)number;
            this.Denominator = (uint)number;
        }
        /// <summary>
        /// Creates new fraction with specified numerator and Denominator.
        /// </summary>
        /// <param name="Numerator">Specifies numerator which should be used for new fraction.</param>
        /// <param name="Denominator">Specifies Denominator which should be used for new fraction.</param>
        public Fraction(int Numerator, uint Denominator)
        {
            this.Numerator = Numerator;
            this.Denominator = Denominator;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Fraction"/> class.
        /// </summary>
        /// <param name="Numerator">The numerator.</param>
        /// <param name="Denominator">The denominator.</param>
        public Fraction(double Numerator, double Denominator)
        {
            Fraction n = Fraction.FromDecimal(Numerator, Denominator);
            this.Numerator = n.Numerator;
            this.Denominator = n.Denominator;
        }
    }
}
